package rwt.server.mtl;

import java.io.IOException;
import java.util.Map;

import org.codehaus.jackson.*;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.*;

import rwt.server.core.BaseMessage;

/**
 * Converts BaseMessages to and from JSON strings. Uses the high-performance Jackson JSON processor.
 */
public class JsonConverter
{
	private static final Logger logger = LoggerFactory.getLogger(JsonConverter.class);
	
	private Map<Integer, Class<? extends BaseMessage>> idToClassMap;
	private JsonFactory factory = new JsonFactory();
	private ObjectMapper mapper = new ObjectMapper(factory);
	
	public JsonConverter(Map<Integer, Class<? extends BaseMessage>> idToClassMap)
	{
		this.idToClassMap = idToClassMap;
	}
	
	/**
	 * Converts a JSON string to a message.
	 * 
	 * @param json JSON-formatted text
	 * @return A message object
	 */
	public BaseMessage fromJson(String json) throws IOException
	{
		int id = extractId(json);
		
		Class<? extends BaseMessage> messageClass = idToClassMap.get(id);
		if (messageClass == null)
		{
			logger.warn("JSON message id '" + id + "' is not bound to a message class");
			return null;
		}
		
		BaseMessage msg = mapper.readValue(json, messageClass);
		return msg;
	}
	
	public String toJson(BaseMessage message) throws IOException
	{
		if (message.id == BaseMessage.INVALID_ID)
			throw new IOException("Message ID is invalid");
		
		return mapper.writeValueAsString(message);
	}
	
	// Private methods =========================================================
	
	private int extractId(String json) throws IOException, JsonParseException
	{
		int id = 0;
		try (JsonParser parser = factory.createJsonParser(json))
		{
			JsonToken token;
			while ((token = parser.nextToken()) != null)
			{
				if (token == JsonToken.FIELD_NAME && parser.getCurrentName().equals("id"))
				{
					parser.nextToken();
					id = parser.getValueAsInt();
					break;
				}
			}
		}
		return id;
	}
}
